package actors

import (
	"context"
	"fmt"
	"sync"
	"time"
	proto "xagent/proto/v1"

	"xagent/internal/knowledge"
	"xagent/internal/memory"
	"xagent/internal/providers"
	"xagent/internal/resource"
	"xagent/internal/task"
	"xagent/internal/types"

	"github.com/asynkron/protoactor-go/actor"
	"github.com/sirupsen/logrus"
)

// TeamCoordinator manages the team of agents
type TeamCoordinator struct {
	*AIAgent
	productManager *actor.PID
	architect      *actor.PID
	projectManager *actor.PID
	engineers      []*actor.PID
	system         *actor.ActorSystem
	teamMetrics    *TeamMetrics
	activeSessions map[string]*proto.CollaborationSession
	knowledgeBase  map[string]*proto.KnowledgeShare
	teamSyncs      []*proto.TeamSync
	sessionMutex   sync.RWMutex
	knowledgeMutex sync.RWMutex
	mutex          sync.RWMutex
	llmProvider    providers.LLMProvider
	memoryManager  memory.MemoryManager
	taskManager    *task.TaskManager
}

// TeamMetrics holds team performance metrics
type TeamMetrics struct {
	TasksCompleted  int
	TasksFailed     int
	AverageTaskTime time.Duration
	TeamUtilization float64
	ResponseTimes   map[string][]time.Duration
	LastUpdate      time.Time
}

// NewTeamCoordinator creates a new team coordinator
func NewTeamCoordinator(name string, system *actor.ActorSystem, llm providers.LLMProvider) *TeamCoordinator {
	// Create required dependencies
	memMgr := memory.NewInMemoryManager()

	taskMgr := task.NewTaskManager(memMgr)
	kb := knowledge.NewRAGEngine(&knowledge.Config{
		EmbeddingModel: "text-embedding-ada-002",
		VectorStore:    "inmemory",
	})
	if kb == nil {
		logrus.Fatal("Failed to create RAG engine: OPENAI_API_KEY not set")
	}

	coordinator := &TeamCoordinator{
		AIAgent:        NewAIAgent(name, "Team Coordinator"),
		system:         system,
		engineers:      make([]*actor.PID, 0),
		teamMetrics:    &TeamMetrics{},
		activeSessions: make(map[string]*proto.CollaborationSession),
		knowledgeBase:  make(map[string]*proto.KnowledgeShare),
		teamSyncs:      make([]*proto.TeamSync, 0),
		llmProvider:    llm,
		memoryManager:  memMgr,
		taskManager:    taskMgr,
	}

	// Set up AI agent dependencies
	coordinator.AIAgent.SetLLMProvider(llm)
	coordinator.AIAgent.SetKnowledgeBase(kb)
	scheduler := task.NewScheduler()
	coordinator.AIAgent.SetScheduler(scheduler)
	coordinator.AIAgent.SetResourceManager(resource.NewManager(5))

	return coordinator
}

// Initialize sets up the team
func (tc *TeamCoordinator) Initialize() {
	tc.AIAgent.Initialize()
	tc.setupTeam()
}

// ProcessTask overrides AIAgent's ProcessTask
func (tc *TeamCoordinator) ProcessTask(task *proto.Task) {
	tc.logger.WithFields(logrus.Fields{
		"task_id":   task.Id,
		"task_name": task.Name,
	}).Info("Team coordinator processing task")

	// Process with AI to enhance task
	go func() {
		ctx := context.Background()
		if response, err := tc.ProcessWithAI(ctx, task.Description); err != nil {
			tc.logger.WithError(err).Error("Failed to process task with AI")
		} else {
			// Create enhanced task
			enhancedTask := &proto.Task{
				Id:          task.Id,
				Name:        task.Name,
				Description: response,
				Status:      proto.TaskStatus_TASK_STATUS_PENDING,
			}

			// Assign to appropriate team member
			tc.assignTask(enhancedTask)
		}
	}()
}

// InitiateCollaboration starts a new collaboration session
func (tc *TeamCoordinator) InitiateCollaboration(req *proto.CollaborationRequest) (*proto.CollaborationSession, error) {
	session := &proto.CollaborationSession{
		Id:           fmt.Sprintf("session-%d", time.Now().UnixNano()),
		Topic:        req.Topic,
		Participants: req.Participants,
		StartTime:    time.Now().UnixNano(),
		Status:       "active",
	}

	tc.sessionMutex.Lock()
	tc.activeSessions[session.Id] = session
	tc.sessionMutex.Unlock()

	tc.notifyParticipants(session)
	return session, nil
}

// ShareKnowledge shares knowledge with the team
func (tc *TeamCoordinator) ShareKnowledge(knowledge *proto.KnowledgeShare) error {
	tc.knowledgeMutex.Lock()
	tc.knowledgeBase[knowledge.Id] = knowledge
	tc.knowledgeMutex.Unlock()

	tc.broadcastKnowledge(knowledge)
	return nil
}

// InitiateTeamSync starts a new team sync
func (tc *TeamCoordinator) InitiateTeamSync(sync *proto.TeamSync) error {
	tc.mutex.Lock()
	tc.teamSyncs = append(tc.teamSyncs, sync)
	tc.mutex.Unlock()

	tc.notifyTeamSync(sync)
	return nil
}

// UpdateMetrics updates the team performance metrics
func (tc *TeamCoordinator) UpdateMetrics(taskDuration time.Duration, isSuccess bool) {
	tc.mutex.Lock()
	defer tc.mutex.Unlock()

	if isSuccess {
		tc.teamMetrics.TasksCompleted++
	} else {
		tc.teamMetrics.TasksFailed++
	}

	// Update average task time
	currentTotal := tc.teamMetrics.AverageTaskTime * time.Duration(tc.teamMetrics.TasksCompleted-1)
	tc.teamMetrics.AverageTaskTime = (currentTotal + taskDuration) / time.Duration(tc.teamMetrics.TasksCompleted)
	tc.teamMetrics.LastUpdate = time.Now()
}

// Internal methods

func (tc *TeamCoordinator) setupTeam() {
	tc.logger.Info("Setting up team")

	// Initialize response times tracking
	tc.teamMetrics.ResponseTimes = make(map[string][]time.Duration)
	tc.teamMetrics.LastUpdate = time.Now()

	// Additional team setup logic can be added here
}

func (tc *TeamCoordinator) assignTask(task *proto.Task) {
	if len(tc.engineers) == 0 {
		tc.logger.Error("No engineers available for task assignment")
		return
	}

	// Calculate next engineer index using task ID as a simple hash
	engineerIndex := len(task.Id) % len(tc.engineers)
	selectedEngineer := tc.engineers[engineerIndex]

	message := &proto.AgentMessage{
		Id:   task.Id,
		Type: proto.MessageType_MESSAGE_TYPE_DIRECT,
		Content: &proto.AgentMessage_Message{
			Message: task.Description,
		},
		Sender: tc.GetRole(),
	}

	// Send task to selected engineer
	tc.SendAgentMessage(selectedEngineer, message)

	startTime := time.Now()
	go func() {
		// Monitor task completion and update metrics
		time.Sleep(5 * time.Second) // Simulated task execution time
		tc.UpdateMetrics(time.Since(startTime), true)
	}()
}

func (tc *TeamCoordinator) notifyParticipants(session *proto.CollaborationSession) {
	if session == nil {
		tc.logger.Error("Cannot notify participants: session is nil")
		return
	}

	message := &proto.AgentMessage{
		Id:   session.Id,
		Type: proto.MessageType_MESSAGE_TYPE_COLLABORATION,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("New collaboration session: %s", session.Topic),
		},
		Sender: tc.GetRole(),
	}

	for _, participant := range session.Participants {
		if pid := tc.getAgentPID(participant); pid != nil {
			tc.SendAgentMessage(pid, message)
		} else {
			tc.logger.WithField("participant", participant).Warn("Participant PID not found")
		}
	}
}

func (tc *TeamCoordinator) broadcastKnowledge(knowledge *proto.KnowledgeShare) {
	message := &proto.AgentMessage{
		Id:   knowledge.Id,
		Type: proto.MessageType_MESSAGE_TYPE_KNOWLEDGE_SHARE,
		Content: &proto.AgentMessage_Message{
			Message: knowledge.Content,
		},
		Sender: tc.GetRole(),
	}

	for _, pid := range tc.getAllAgentPIDs() {
		tc.SendAgentMessage(pid, message)
	}
}

func (tc *TeamCoordinator) notifyTeamSync(sync *proto.TeamSync) {
	message := &proto.AgentMessage{
		Id:   sync.Id,
		Type: proto.MessageType_MESSAGE_TYPE_TEAM_SYNC,
		Content: &proto.AgentMessage_Message{
			Message: fmt.Sprintf("Team sync: %s", sync.Type),
		},
		Sender: tc.GetRole(),
	}

	for _, participant := range sync.Participants {
		if pid := tc.getAgentPID(participant); pid != nil {
			tc.SendAgentMessage(pid, message)
		}
	}
}

func (tc *TeamCoordinator) getAgentPID(role string) *actor.PID {
	switch role {
	case "product_manager":
		return tc.productManager
	case "architect":
		return tc.architect
	case "project_manager":
		return tc.projectManager
	default:
		return nil
	}
}

func (tc *TeamCoordinator) getAllAgentPIDs() []*actor.PID {
	pids := []*actor.PID{tc.productManager, tc.architect, tc.projectManager}
	pids = append(pids, tc.engineers...)
	return pids
}

// ProcessWithAI processes a task with AI
func (tc *TeamCoordinator) ProcessWithAI(ctx context.Context, taskDesc string) (string, error) {
	// ... implementation using taskDesc instead of task ...
	return "", nil
}

// SendMessage sends a message to an agent
func (tc *TeamCoordinator) SendAgentMessage(target *actor.PID, msg *proto.AgentMessage) {
	tc.GetContext().Send(target, msg)
}

// createAgent creates a new agent of the specified type
func (tc *TeamCoordinator) createAgent(agentType string) (types.Agent, error) {
	switch agentType {
	case "ai":
		agent := NewAIAgent("ai_agent", "AI-powered agent for complex tasks")

		// Set up agent dependencies
		if tc.llmProvider != nil {
			agent.SetLLMProvider(tc.llmProvider)
		}
		scheduler := task.NewScheduler()
		agent.SetScheduler(scheduler)
		agent.SetResourceManager(resource.NewManager(5))

		return agent, nil
	default:
		return nil, fmt.Errorf("unknown agent type: %s", agentType)
	}
}
